/**
 * #info
 *
 *@author   wuwg
 *@createTime   2018/03/20
 *@updateTime   2018/05/10
 *@description  color-picker  component   颜色选择组件
 */

/**
 * #item
 *
 * @type        aty-color-picker
 * @cn          颜色选择器
 * @menu        工具
 * @stPlugin    true
 * @stContainer false
 */

import config from '../../../config/config.js'

//  tools
import tinycolor from 'tinycolor2'
// directives
import clickoutside from '../../../directives/clickoutside'
import TransferDom from '../../../directives/transfer-dom'
// components
import Drop from '../../form/select/dropdown/index.vue'
import Confirm from '../../form/date-picker/base/confirm.vue'
import RecommendColors from './recommend-colors/index.vue'
import Saturation from './saturation/index.vue'
import Hue from './hue/index.vue'
import Alpha from './alpha/index.vue'
// mixins
import Emitter from '../../../mixins/emitter.js'
// prefix
const prefixCls = config.prefix + '-color-picker'
const inputPrefixCls = config.prefix + '-input'

function _colorChange (data, oldHue) {
  data = data === '' ? '#2d8cf0' : data
  const alpha = data && data.a
  let color
  // hsl is better than hex between conversions
  if (data && data.hsl) {
    color = tinycolor(data.hsl)
  } else if (data && data.hex && data.hex.length > 0) {
    color = tinycolor(data.hex)
  } else {
    color = tinycolor(data)
  }

  if (color && (color._a === undefined || color._a === null)) {
    color.setAlpha(alpha || 1)
  }

  const hsl = color.toHsl()
  const hsv = color.toHsv()

  if (hsl.s === 0) {
    hsv.h = hsl.h = data.h || (data.hsl && data.hsl.h) || oldHue || 0
  }

  /*
   * when the hsv.v is less than 0.0164 (base on test)
   * because of possible loss of precision
   * the result of hue and saturation would be miscalculated
   */
  if (hsv.v < 0.0164) {
    hsv.h = data.h || (data.hsv && data.hsv.h) || 0
    hsv.s = data.s || (data.hsv && data.hsv.s) || 0
  }

  if (hsl.l < 0.01) {
    hsl.h = data.h || (data.hsl && data.hsl.h) || 0
    hsl.s = data.s || (data.hsl && data.hsl.s) || 0
  }

  return {
    hsl: hsl,
    hex: color.toHexString().toUpperCase(),
    rgba: color.toRgb(),
    hsv: hsv,
    oldHue: data.h || oldHue || hsl.h,
    source: data.source,
    a: data.a || color.getAlpha()
  }
}

export default {
  name: prefixCls,
  mixins: [Emitter],
  components: {
    Drop,
    Confirm,
    RecommendColors,
    Saturation,
    Hue,
    Alpha
  },
  directives: { clickoutside, TransferDom },
  props: {
    /**
     * #property
     *
     * @name     name
     * @cn      属性名称
     * @type    string
     * @description 值对应的属性名
     */
    name: {
      type: String
    },
    /**
     * #property
     *
     * @name    v-model
     * @cn      绑定数据
     * @type    string
     * @description VUE的v-model,提供数据对象的双向绑定
     */
    /**
     * #property
     *
     * @name    value
     * @cn      绑定的值
     * @type    string
     * @description 绑定的值，可使用 v-model 双向绑定
     */
    value: {
      type: String,
      default: ''
    },

    /**
     * #property
     *
     * @name    hue
     * @cn      是否支持色彩选择
     * @type    boolean
     * @value  true
     * @bind  must
     * @description 是否支持色彩选择
     */
    hue: {
      type: Boolean,
      default: true
    },
    /**
     * #property
     *
     * @name    alpha
     * @cn      是否支持透明度选择
     * @type    boolean
     * @value  false
     * @description 是否支持透明度选择
     */
    alpha: {
      type: Boolean,
      default: false
    },

    /**
     * #property
     *
     * @name    recommend
     * @cn      是否显示推荐的颜色预设
     * @type    boolean
     * @value  false
     * @description 是否显示推荐的颜色预设
     */
    recommend: {
      type: Boolean,
      default: false
    },

    /**
     * #property
     *
     * @name    format
     * @cn      颜色的格式
     * @type    list
     * @options [hsl,hsv,hex,rgb]
     * @description 该颜色选择的颜色格式，取值为 [hsl、hsv、hex、rgb]中的其中一个
     */
    format: {
      validator (value) {
        return Artery.isOneOf(value, ['hsl', 'hsv', 'hex', 'rgb'])
      }
    },

    /**
     * #property
     *
     * @name    colors
     * @cn      自定义颜色预设
     * @type    array
     * @bind    must
     * @description 自定义颜色预设，Array 格式
     */
    colors: {
      type: Array,
      default () {
        return []
      }
    },
    /**
     * #property
     *
     * @name    disabled
     * @cn      是否禁用
     * @type    boolean
     * @value  false
     * @description 该颜色选择器是否禁用
     */
    disabled: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    placement
     * @cn      颜色选择器出现的位置
     * @type    list
     * @options [top, top-start, top-end,
     * bottom, bottom-start, bottom-end,
     * left, left-start, left-end,
     * right, right-start, right-end]
     * @value  bottom
     * @description 该颜色选择器出现的位置，取值为  [top, top-start, top-end,
     * bottom, bottom-start, bottom-end,
     * left, left-start, left-end,
     * right, right-start, right-end]中的其中一个
     */
    placement: {
      validator (value) {
        return Artery.isOneOf(value, [
          'top', 'top-start', 'top-end',
          'bottom', 'bottom-start', 'bottom-end',
          'left', 'left-start', 'left-end',
          'right', 'right-start', 'right-end'])
      },
      default: 'bottom'
    },
    /**
     * #property
     *
     * @name    transfer
     * @cn      是否将弹层放置于 body 内
     * @type    boolean
     * @value  false
     * @description  是否将弹层放置于 body 内，
     *              在 Tabs、带有 fixed 的 Table 列内使用时，建议添加此属性，它将不受父级样式影响，从而达到更好的效果
     */
    transfer: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      val: _colorChange(this.value),
      currentValue: this.value,
      prefixCls: prefixCls,
      visible: false,
      disableCloseUnderTransfer: false, // transfer 模式下，点击Drop也会触发关闭
      recommendedColor: [
        '#2d8cf0',
        '#19be6b',
        '#ff9900',
        '#ed3f14',
        '#00b5ff',
        '#19c919',
        '#f9e31c',
        '#ea1a1a',
        '#9b1dea',
        '#00c2b1',
        '#ac7a33',
        '#1d35ea',
        '#8bc34a',
        '#f16b62',
        '#ea4ca3',
        '#0d94aa',
        '#febd79',
        '#5d4037',
        '#00bcd4',
        '#f06292',
        '#cddc39',
        '#607d8b',
        '#000000',
        '#ffffff'
      ]
    }
  },
  watch: {
    value (newVal) {
      this.val = _colorChange(newVal)
    },
    visible (val) {
      this.val = _colorChange(this.value)
      if (val) {
        this.$refs.drop.update()
      } else {
        this.$refs.drop.destroy()
      }
    }
  },
  computed: {
    transition () {
      if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') {
        return 'slide-up'
      } else {
        return 'fade'
      }
    },
    saturationColors: {
      get () {
        return this.val
      },
      set (newVal) {
        this.val = newVal
        /**
         * #event
         *
         * @name    active-change
         * @param   color   当前的颜色值
         * @param   name    当前值对应的name属性
         * @body
         * @description    面板中当前显示的颜色发生改变时触发
         */
        this.$emit('active-change', this.formatColor, this.name)
      }
    },
    classes () {
      return [
                `${prefixCls}`,
                {
                  [`${prefixCls}-transfer`]: this.transfer
                }
      ]
    },
    wrapClasses () {
      return [
                `${prefixCls}-rel`,
                `${inputPrefixCls}-wrapper`,
                {
                  [`${this.prefixCls}-disabled`]: this.disabled
                }
      ]
    },
    inputClasses () {
      return [
                `${prefixCls}-input`,
                `${inputPrefixCls}`,
                {
                  [`${inputPrefixCls}-disabled`]: this.disabled,
                  [`${inputPrefixCls}-focused`]: this.visible
                }
      ]
    },
    dropdownClass () {
      return [
                `${config.prefix}-icon`,
                `${config.prefix}-icon-arrow-down-b`,
                `${config.prefix}-input-icon`,
      ]
    },
    emptyClass () {
      return [
                `${config.prefix}-icon`,
                `${config.prefix}-icon-ios-close-empty`
      ]
    },
    dropMaxHeightClass () {
      return `${config.prefix}-transfer-no-max-height`
    },
    displayedColor () {
      let color
      if (this.visible) {
        const rgba = this.saturationColors.rgba
        color = {
          r: rgba.r,
          g: rgba.g,
          b: rgba.b,
          a: rgba.a
        }
      } else {
        color = tinycolor(this.value).toRgb()
      }
      return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
    },
    formatColor () {
      const value = this.saturationColors
      const format = this.format
      let color

      const rgba = `rgba(${value.rgba.r}, ${value.rgba.g}, ${value.rgba.b}, ${value.rgba.a})`
      if (format) {
        if (format === 'hsl') {
          color = tinycolor(value.hsl).toHslString()
        } else if (format === 'hsv') {
          color = tinycolor(value.hsv).toHsvString()
        } else if (format === 'hex') {
          color = value.hex
        } else if (format === 'rgb') {
          color = rgba
        }
      } else if (this.alpha) {
        color = rgba
      } else {
        color = value.hex
      }
      return color
    }
  },
  methods: {
    // 开启 transfer 时，点击 Drop 即会关闭，这里不让其关闭
    handleTransferClick () {
      if (this.transfer) this.disableCloseUnderTransfer = true
    },
    handleClose () {
      if (this.disableCloseUnderTransfer) {
        this.disableCloseUnderTransfer = false
        return false
      }
      this.visible = false
    },
    toggleVisible () {
      if (this.disabled) {
        return
      }
      this.visible = !this.visible
    },
    childChange (data) {
      this.colorChange(data)
    },
    colorChange (data, oldHue) {
      this.oldHue = this.saturationColors.hsl.h
      this.saturationColors = _colorChange(data, oldHue || this.oldHue)
    },
    isValidHex (hex) {
      return tinycolor(hex).isValid()
    },
    simpleCheckForValidColor (data) {
      const keysToCheck = ['r', 'g', 'b', 'a', 'h', 's', 'l', 'v']
      let checked = 0
      let passed = 0

      for (let i = 0; i < keysToCheck.length; i++) {
        const letter = keysToCheck[i]
        if (data[letter]) {
          checked++
          if (!isNaN(data[letter])) {
            passed++
          }
        }
      }

      if (checked === passed) {
        return data
      }
    },
    handleSuccess () {
      const color = this.formatColor
      this.currentValue = color
      this.$emit('input', color)
      /**
       * #event
       *
       * @name    change
       * @param   color   当前的颜色值
       * @param   name    当前值对应的name属性
       * @body
       * @description    当绑定值变化时触发
       */
      this.$emit('change', color, this.name)
      this.handleClose()
    },
    handleClear () {
      this.currentValue = ''
      this.$emit('input', '')
      this.$emit('change', '')
      this.handleClose()
    },
    handleSelectColor (color) {
      this.val = _colorChange(color)
    }
  }
}
